package cn.minimelon.api.service.data.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.minimelon.api.domain.common.LoginUser;
import cn.minimelon.api.domain.common.PageQuery;
import cn.minimelon.api.domain.common.TableDataInfo;
import cn.minimelon.api.domain.data.*;
import cn.minimelon.api.domain.enums.AuditFlagEnum;
import cn.minimelon.api.domain.enums.SwTypeEnum;
import cn.minimelon.api.domain.module.vo.DataPropVO;
import cn.minimelon.api.mapper.data.CodeApplyMapper;
import cn.minimelon.api.service.data.CodeApplyService;
import cn.minimelon.api.service.data.ModelDeployService;
import cn.minimelon.api.service.data.utils.DataQueryUtils;
import cn.minimelon.api.utils.PageUtils;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.solon.annotation.Db;
import org.noear.snack.ONode;
import org.noear.solon.annotation.Component;
import org.noear.solon.annotation.Inject;
import org.noear.solon.core.util.TypeReference;
import org.noear.solon.data.annotation.Transaction;

import java.math.BigInteger;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
@Component
public class CodeApplyServiceImpl implements CodeApplyService {
    @Db
    private CodeApplyMapper codeApplyMapper;

    @Inject
    private ModelDeployService modelDeployService;

    @Override
    public TableDataInfo selectApplyPage(PageQuery<CodeIndexVO> pageQuery, LoginUser user) {
        CodeIndexVO queryParam = pageQuery.getQueryParam();
        QueryWrapper<CodeIndexVO> queryWrapper = new QueryWrapper<>();
        DataQueryUtils.collectQueryCond(queryParam.getModelCode(), queryParam.getCondList(), user);
        DataQueryUtils.buildCustomCond(queryWrapper, queryParam.getCondList());
        Map<String, DataPropVO> hideCodeHash = getHideHash(queryParam.getModelCode());
        buildAuthCond(queryWrapper, user, queryParam.getModelCode());
        PageUtils.startPage(pageQuery);
        log.info("modelCode : {}", queryParam.getModelCode());
        List<CodeIndexVO> idxList = codeApplyMapper.selectIndexByCond(queryWrapper, queryParam.getModelCode());
        TableDataInfo pageResult = PageUtils.endPage(idxList);
        if (CollUtil.isNotEmpty(pageResult.getRows())) {
            List<BigInteger> ids = idxList.stream().map(CodeIndexVO::getUid).collect(Collectors.toList());
            List<CodeValuePO> vvList = codeApplyMapper.selectValueByIds(ids, queryParam.getModelCode());
            for (CodeIndexVO idx : idxList) {
                vvList.stream().filter(it -> ObjUtil.equals(it.getUid(), idx.getUid()))
                        .findFirst().ifPresent(vv -> flatDataJson(idx, vv, hideCodeHash));
            }
        }
        return pageResult;
    }

    private void flatDataJson(CodeIndexVO idx, CodeValuePO vv, Map<String, DataPropVO> hash) {
        TypeReference<Map<String, CodeDataVO>> typeRef = new TypeReference<Map<String, CodeDataVO>>() {};
        Map<String, CodeDataVO> dataMap = ONode.deserialize(vv.getData(), typeRef.getType());
        Map<String, String> dataHash = new HashMap<>();
        for(Map.Entry<String, DataPropVO> entry : hash.entrySet()) {
            DataPropVO pp = entry.getValue();
            CodeDataVO pv = dataMap.get(pp.getHideCode());
            if (pv != null) {
                String showValue = StrUtil.emptyToDefault(pv.getSv(), pv.getVv());
                String colCode = StrUtil.toCamelCase(pp.getColCode());
                log.info("h:{}, s:{} v:{}", pp.getHideCode(), colCode, showValue);
                if (StrUtil.equals(pp.getAdvanceSw(), SwTypeEnum.YES.val()) && StrUtil.isNotEmpty(showValue)) {
                    BeanUtil.setFieldValue(idx, colCode, showValue);
                }
                dataHash.put(pp.getHideCode(), showValue);
            }
        }
        idx.setDataHash(dataHash);
        idx.setRevision(vv.getRevision());
    }

    @Transaction
    @Override
    public CodeBaseVO saveUIListData(ApplySaveVO applySave, LoginUser user) {
        ModelQueryVO query = new ModelQueryVO();
        query.setAreaCode(applySave.getAreaCode());
        query.setModelCode(applySave.getModelCode());
        applySave.setModelInfo(modelDeployService.selectFullInfo(query));
        boolean isAdd = applySave.getUid() == null;
        CodeIndexPO codeIndex = isAdd ? execAddCOdeData(applySave, user) : execUpdateCodeData(applySave, user);
        if (applySave.getActionType() == SwTypeEnum.YES.code()) {
            // 此处执行校验并提交操作
            log.info("start flow submit:{}", codeIndex.getUid());
        }
        return codeIndex;
    }

    private CodeIndexPO execAddCOdeData(ApplySaveVO applySave, LoginUser user) {
        long uid = IdUtil.getSnowflake(user.getUserId()).nextId();
        String userId = String.valueOf(user.getUserId());
        Date now = new Date();
        log.info("insert code index start : {}", uid);
        CodeIndexPO codeIndex = new CodeIndexPO();
        codeIndex.setTypeSrc(applySave.getTypeSrc());
        codeIndex.setModelCode(applySave.getModelCode());
        codeIndex.setUid(BigInteger.valueOf(uid));
        codeIndex.setRevision(0);
        codeIndex.setCreateBy(userId);
        codeIndex.setCreateTime(now);
        codeIndex.setUpdateBy(userId);
        codeIndex.setUpdateTime(now);
        codeIndex.setTenantId(user.getUser().getTenantId());
        // 设置自定义字段
        initIndexFromApplyInfo(codeIndex, applySave);
        // 设置内置字段
        codeIndex.setTaskState(AuditFlagEnum.SAVE.getVal());
        codeIndex.setMvs(applySave.getModelInfo().getVersion());
        codeIndex.setDataFlag(10); // 需要枚举：有效
        int count = codeApplyMapper.insertCodeIndex(codeIndex);
        log.info("insert code index end : {}", ONode.stringify(codeIndex));
        if (count > 0) {
            log.info("insert code value start : {}", codeIndex.getUid());
            CodeValuePO codeValue = new CodeValuePO();
            BeanUtil.copyProperties(codeIndex, codeValue);
            initValueFromApplyInfo(codeValue, applySave);
            codeApplyMapper.insertCodeValue(codeValue);
            log.info("insert code value end: {}", codeIndex.getUid());
        }
        return codeIndex;
    }

    private CodeIndexPO execUpdateCodeData(ApplySaveVO applySave, LoginUser user) {
        String userId = String.valueOf(user.getUserId());
        Date now = new Date();
        log.info("update code index start : {}", applySave.getUid());
        CodeIndexPO codeIndex = new CodeIndexPO();
        codeIndex.setUid(applySave.getUid());
        codeIndex.setMid(applySave.getUid());
        codeIndex.setRevision(applySave.getRevision());
        codeIndex.setUpdateBy(userId);
        codeIndex.setUpdateTime(now);
        initIndexFromApplyInfo(codeIndex, applySave);
        int count = codeApplyMapper.updateCodeIndex(codeIndex);
        log.info("update code index end : {}", codeIndex.getUid());
        if (count > 0) {
            log.info("update code value start : {}", codeIndex.getUid());
            CodeValuePO codeValue = new CodeValuePO();
            BeanUtil.copyProperties(codeIndex, codeValue);
            initValueFromApplyInfo(codeValue, applySave);
            codeApplyMapper.updateCodeValue(codeValue);
            log.info("update code value end : {}", codeIndex.getUid());
        }
        return codeIndex;
    }

    // 从界面数据，为CodeIndex赋值
    private void initIndexFromApplyInfo(CodeIndexPO codeIndex, ApplySaveVO applySave) {
        List<DataPropVO> pl = applySave.getModelInfo().getPropList();
        // 开启搜索索引的列，写入索引表
        List<DataPropVO> ssList = pl.stream()
                .filter(it -> StrUtil.equals(it.getAdvanceSw(), SwTypeEnum.YES.val()))
                .collect(Collectors.toList());
        for (DataPropVO ss : ssList) {
            Optional<CodeDataVO> opt = applySave.getDataList().stream()
                    .filter(it -> StrUtil.equals(it.getHc(), ss.getHideCode())).findFirst();
            if (opt.isPresent()) {
                String showValue = StrUtil.emptyToDefault(opt.get().getSv(), opt.get().getVv());
                log.info("initIndexFromApplyInfo {} : {}", ss.getColCode(), showValue);
                if (StrUtil.isNotEmpty(showValue)) {
                    BeanUtil.setFieldValue(codeIndex, StrUtil.toCamelCase(ss.getColCode()), showValue);
                }
            } else {
                BeanUtil.setFieldValue(codeIndex, StrUtil.toCamelCase(ss.getColCode()), null);
            }
        }
    }

    // 从界面数据，为CodeValue赋值
    private void initValueFromApplyInfo(CodeValuePO codeValue, ApplySaveVO applySave) {
        List<DataPropVO> pl = applySave.getModelInfo().getPropList();
        Map<String, CodeDataVO> dataMap = new HashMap<>();
        for (DataPropVO pr : pl) {
            Optional<CodeDataVO> opt = applySave.getDataList().stream()
                    .filter(it -> StrUtil.equals(it.getHc(), pr.getHideCode())).findFirst();
            if (opt.isPresent()) {
                String showValue = StrUtil.emptyToDefault(opt.get().getSv(), null);
                String validValue = StrUtil.emptyToDefault(opt.get().getVv(), null);
                dataMap.put(pr.getHideCode(), new CodeDataVO(showValue, validValue));
            } else {
                dataMap.remove(pr.getHideCode());
            }
        }
        codeValue.setData(ONode.stringify(dataMap));
    }

    /**
     * 构造权限条件
     */
    private void buildAuthCond(QueryWrapper<CodeIndexVO> qw, LoginUser user, String modelCode) {
        qw.eq("create_by", user.getUserId());
        if (StrUtil.isNotEmpty(user.getUser().getTenantId())) {
            qw.eq("tenant_id", user.getUser().getTenantId());
        }
    }

    private Map<String, DataPropVO> getHideHash(String modelCode) {
        ModelQueryVO modelQuery = new ModelQueryVO();
        modelQuery.setModelCode(modelCode);
        List<DataPropVO> ppList = modelDeployService.selectPropList(modelQuery);
        return ppList.stream().collect(Collectors.toMap(DataPropVO::getHideCode, Function.identity(), (a, b) -> a));
    }
}
